/**
 * 将url中的查询参数转换为对象
 * @param url url地址
 * @returns
 */
export function queryToObj(url: string) {
  // 定义一个空对象
  const obj: Record<string, string> = {}
  // 将url中的查询参数部分取出
  const query = url.split('?')[1]
  // 如果查询参数存在
  if (query) {
    // 定义一个正则表达式，用于匹配查询参数
    const reg = /([^?&#=]+)=([^?&#=]*)/g
    // 使用正则表达式匹配查询参数，并将匹配到的参数添加到对象中
    query.replace(reg, (rs, $1, $2) => {
      // 解码第一个参数
      const key = decodeURIComponent($1)
      // 解码第二个参数
      const value = decodeURIComponent($2)
      // 将第二个参数转换为字符串
      //   value = String(value)
      // 将解码后的参数添加到对象中
      obj[key] = value
      // 返回结果
      return rs
    })

    // const arr = query.split('&')
    // arr.forEach((item) => {
    //   const [key, value] = item.split('=')
    //   obj[key] = value
    // })
  }
  // 返回对象
  return obj
}

/**
 * 将对象转换为查询字符串
 * @param obj 对象
 * @returns
 */
export function objToQuery(obj: Record<string, string> | null) {
  // 如果对象为空，则返回空字符串
  if (!obj) return ''
  // 遍历对象的键
  return Object.keys(obj)
    .map((key) => {
      // 如果键对应的值为空，则返回空字符串
      if (!obj[key]) return ''
      // 对键和值进行编码，并返回编码后的字符串
      return encodeURIComponent(key) + '=' + encodeURIComponent(obj[key])
    })
    .join('&')
}

/**
 * 获取URL中的指定参数值
 * @param key 参数名
 * @returns
 */
export function getQueryValue(key: string) {
  // 创建一个正则表达式，用于匹配URL中的指定参数值
  const reg = new RegExp('(^|&|\\?)' + key + '=([^&#]*)(&|#|$)')
  // 使用正则表达式匹配URL中的指定参数值
  const r = window.location.href.match(reg)
  // 如果匹配成功，则返回解码后的参数值，否则返回空字符串
  return r ? decodeURIComponent(r[2]) : ''
}

/**
 * 计算字符串的字节长度
 * @param str 字符串
 * @returns
 */
export function byteLength(str: string) {
  // 初始化字符串长度为字符串的长度
  let len = str.length
  // 从字符串的最后一个字符开始遍历
  for (let i = len - 1; i >= 0; i--) {
    // 获取当前字符的Unicode编码
    const code = str.charCodeAt(i)
    // 如果当前字符的Unicode编码大于0x7f且小于等于0x7ff，则长度加1
    if (code > 0x7f && code <= 0x7ff) {
      len++
      // 如果当前字符的Unicode编码大于0x7ff且小于等于0xffff，则长度加2
    } else if (code > 0x7ff && code <= 0xffff) {
      len += 2
    }
    // 如果当前字符的Unicode编码大于等于0xdc00且小于等于0xdfff，则继续遍历前一个字符
    if (code >= 0xdc00 && code <= 0xdfff) {
      i--
    }
  }
  // 返回计算得到的字符串长度
  return len
}

/**
 * 将字符串中的首字母大写
 * @param str 字符串
 * @returns
 */
export function firstUpperCase(str: string) {
  // 使用正则表达式匹配字符串中的首字母，并将其转换为大写
  return str.replace(/( |^)[a-z]/g, (s) => s.toUpperCase())
}

/**
 * 用于生成指定长度的UUID
 * @param length UUID长度
 * @param before 前缀字符串
 * @param after 后缀字符串
 * @returns
 */
export function uuid(length: number, before = '', after = '') {
  // 定义一个包含所有可能字符的字符串
  const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
  // 获取字符串的长度
  const len = chars.length
  // 定义一个空数组，用于存储生成的UUID
  const uids: string[] = []
  // 循环指定次数，生成UUID
  for (let i = 0; i < length; i++) {
    // 在字符串中随机选择一个字符，并将其添加到数组中
    uids[i] = chars.charAt(0 | (Math.random() * len))
  }
  // 返回生成的UUID，并在前后添加指定的字符串
  return before + uids.join('') + after
}

/**
 * 删除对象中的指定键
 * @param obj 对象
 * @param keys 键值数组
 * @returns
 */
export function delTreeKeys(obj: Record<string, any>, keys: string[]) {
  // 遍历指定键数组
  for (const key of keys) {
    // 获取对象中指定键的值
    const element = obj[key]
    // 如果值存在且为对象，则递归调用delTreeKeys函数
    if (element && typeof element === 'object') {
      delTreeKeys(element, keys)
    }
    // 如果指定键数组中包含该键，则删除该键
    keys.includes(key) && delete obj[key]
  }
  // 返回处理后的对象
  return obj
}

interface IObj {
  [key: string]: any
}

/**
 * 将数组转换为树形结构
 * @param data 原始数组
 * @param id id
 * @param pid 父id
 * @param children 子节点名
 * @returns
 */
export function arrayToTree(
  data: any[], // 原始数组
  id?: string, // 每个节点的唯一标识
  pid?: string, // 每个节点的父节点标识
  children?: string // 每个节点的子节点数组
) {
  id = id || 'id' // 默认id为'id'
  pid = pid || 'parentId' // 默认pid为'parentId'
  children = children || 'children' // 默认children为'children'
  const result: any[] = [] // 存储最终结果的数组
  data = JSON.parse(JSON.stringify(data)) // 深拷贝原始数组，防止修改原始数据
  if (!Array.isArray(data)) {
    return result // 如果data不是数组，直接返回空数组
  }
  data.forEach((item) => {
    delete item[children] // 删除每个节点的children属性
  })
  const map: IObj = {} // 存储每个节点的映射关系
  data.forEach((item) => {
    map[item[id]] = item // 将每个节点存储到map中，以id为键
  })
  data.forEach((item) => {
    const parent = map[item[pid]] // 获取当前节点的父节点
    if (parent) {
      parent[children] || (parent[children] = []) // 如果父节点存在，则将当前节点添加到父节点的children数组中
      parent[children].push(item)
    } else {
      result.push(item) // 如果父节点不存在，则将当前节点添加到结果数组中
    }
  })
  return result // 返回最终结果
}

/**
 * 将树形结构转换为数组
 */
export function treeToArray(
  // 树形结构，默认为空数组
  tree: any[] = [],
  // 子节点的属性名，默认为'children'
  children = 'children',
  // 回调函数，可选
  callback?: Function
) {
  // 定义一个空数组，用于存储转换后的结果
  const result: any[] = []
  // 定义一个变量，用于记录当前层级
  let level = 0
  // 定义一个递归函数，用于遍历树形结构
  function dg(obj: IObj) {
    // 如果有回调函数，则执行回调函数
    callback && callback(obj, result, level)
    // 当前层级加一
    ++level
    // 如果当前节点有子节点，则将子节点添加到结果数组中，并递归遍历子节点
    if (obj[children]) {
      result.push(...obj[children])
      obj[children].forEach((item: IObj) => {
        dg(item)
      })
    }
    // 当前层级减一
    --level
  }
  // 如果传入的树形结构是一个数组，则遍历数组中的每个节点，并递归遍历子节点
  if (Array.isArray(tree)) {
    result.push(...tree)
    tree.forEach((e) => dg(e))
  } else {
    // 如果传入的树形结构不是一个数组，则直接递归遍历节点
    result.push(tree)
    dg(tree)
  }
  // 返回转换后的结果数组
  return result
}

/**
 * 获取浏览器类型
 * @param userAgent userAgent字符串
 * @returns
 */
export function getBrowser(userAgent: string) {
  // 定义一个函数，用于判断userAgent中是否包含指定的字符串
  const isExplorer = (exp: string) => userAgent.indexOf(exp) > -1

  // 判断userAgent中是否包含MSIE或Trident/7.0，如果是，则返回'IE'
  if (isExplorer('MSIE') || isExplorer('Trident/7.0')) {
    return 'IE'
    // 判断userAgent中是否包含Firefox，如果是，则返回'Firefox'
  } else if (isExplorer('Firefox')) {
    return 'Firefox'
    // 判断userAgent中是否包含Chrome，如果是，则返回'Chrome'
  } else if (isExplorer('Chrome')) {
    return 'Chrome'
    // 判断userAgent中是否包含Opera，如果是，则返回'Opera'
  } else if (isExplorer('Opera')) {
    return 'Opera'
    // 判断userAgent中是否包含Safari，如果是，则返回'Safari'
  } else if (isExplorer('Safari')) {
    return 'Safari'
    // 如果以上条件都不满足，则返回'Unknown'
  } else {
    return 'Unknown'
  }
}
